﻿using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using System.Windows.Threading;
using RawInputProcessor.Win32;
using Red.CommUtils.Application;
using Red.Controls.Shell;
using Red.Controls.ViewModels;
using Red.EngineData.View;

namespace Red.Controls
{
    public partial class BackgroundlessPanel : System.Windows.Forms.Control
    {
        protected override void OnPaintBackground( System.Windows.Forms.PaintEventArgs e )
        {
            // empty implementation to reduce flickering
        }
    }

    /// <summary>
    /// Interaction logic for EnginePreviewPanel.xaml
    /// </summary>
    public class EnginePreviewPanel : WindowsFormsHost
    {
        #region Data

        private Notifier _contexMenuNotifier;
        private OverlayControl _overlay;
        static private bool _doInitializeRawInput = true;

        #endregion Data

        #region Properties

        private static readonly DependencyProperty OverlayContentProperty = 
            DependencyProperty.Register( "OverlayContent", typeof( object ), typeof( EnginePreviewPanel ) );

        public UIElement OverlayContent
        {
            get { return (UIElement)GetValue( OverlayContentProperty ); }
            set { SetValue( OverlayContentProperty, value ); }
        }

        public new IntPtr Handle
        {
            get { return Child.Handle; }
        }

        #endregion Properties

        #region C-tor

        public EnginePreviewPanel()
        {
            Child = new BackgroundlessPanel();
            _overlay = new OverlayControl( this );

            BindingOperations.SetBinding( _overlay, OverlayControl.OverlayContentProperty,
                new System.Windows.Data.Binding( "OverlayContent" ) { Source = this } );

            LazyRawInputInit();
 
            IsVisibleChanged += EnginePreviewPanel_IsVisibleChanged;
        }

        #endregion C-tor

        #region Public methods

        public void ShowError( string errorTitle, string errorDescription, string details )
        {
            _overlay.ShowError( errorTitle, errorDescription, details );
        }

        public void HideError()
        {
            _overlay.HideError();
        }

        #endregion Public methods

        #region Private methods

        private void LazyRawInputInit()
        {
            if ( _doInitializeRawInput )
            {
                // We need to register this application to receive and route WM_INPUT messages for few devices for engine application
                // (for example, mouse is registered by windows forms by default (as seen when experimenting), but keyboard is not)

                _doInitializeRawInput = false;

                RawInputDevice[] keyboardDevice = new RawInputDevice[1];
                keyboardDevice[0].UsagePage = HidUsagePage.GENERIC;
                keyboardDevice[0].Usage = HidUsage.Keyboard;
                keyboardDevice[0].Flags = 0;
                keyboardDevice[0].Target = IntPtr.Zero;
                Win32Methods.RegisterRawInputDevices( keyboardDevice, 1, (uint)Marshal.SizeOf( keyboardDevice[0] ) );
            }
        }

        private async Task ResetInterop()
        {
            DataItem item = DataContext as DataItem;
            if ( item != null )
            {
                try
                {
                    InteropDataItem previewItem = await item.DigUpSubItemAsync( "preview" ) as InteropDataItem;

                    previewItem.Data.RequestNotifier( "showEditorContextMenu", notifier =>
                    {
                        _contexMenuNotifier = notifier;
                        _contexMenuNotifier.OnNotifyEvent += ContexMenuNotifier_OnNotifyEvent;
                    } );

                    _overlay.DataContext = previewItem;
                }
                catch ( ProtocolException ex )
                {
                    RedMainWindow localTopWindow = Window.GetWindow( this ) as RedMainWindow;
                    if ( localTopWindow != null )
                    {
                        await localTopWindow.ShowErrorAsync( ex.Message );
                    }
                }
            }
        }

        #endregion Private methods

        #region Event handlers

        protected async override void OnPropertyChanged( DependencyPropertyChangedEventArgs e )
        {
             base.OnPropertyChanged( e );
             if ( e.Property == DataContextProperty )
             {
                 await ResetInterop();
             }
        }

        private void ContexMenuNotifier_OnNotifyEvent( string evt )
        {
            Dispatcher.BeginInvoke( (Action)delegate
            {
                if ( ContextMenu != null )
                {
                    ContextMenu.IsOpen = true;
                    // this allows ContextMenu Commands to reach their CommandBindings
                    // https://social.msdn.microsoft.com/Forums/vstudio/en-US/7bd75a7c-eab4-4f3a-967b-94a9534a7455/why-is-my-contextmenu-item-disabled?forum=wpf
                    ContextMenu.PlacementTarget = this; 
                }
            } );
        }

        private void EnginePreviewPanel_IsVisibleChanged( object sender, DependencyPropertyChangedEventArgs e )
        {
            if ( true.Equals( e.NewValue ) )
                _overlay.Show();
            else
                _overlay.Hide();
        }

        #endregion Event handlers
    }
}
